"""稀疏矩阵是一种特殊的矩阵，其非零元素数目远远少于零元素数目，并且非零元素分布没有规律。
这种矩阵在实际应用中经常出现，例如在物理学、图形学和网络通信等领域。

稀疏矩阵其实也可以和一般的矩阵一样处理，之所以要把它区分开来进行特殊处理，是因为：
一方面稀疏矩阵的存储空间开销通常比稠密矩阵要小得多，可以节省存储空间；
另一方面，在计算稀疏矩阵时，可以利用其特殊的结构，采用专门的算法，提高计算效率和准确性。
因此，稀疏矩阵在Scipy库中被单独作为一个模块，以便被更好地处理和应用。

1. 主要功能
稀疏矩阵子模块（scipy.sparse）的主要功能包括：

类别	说明
稀疏数组类	支持各种格式的稀疏数组
稀疏矩阵类	支持各种格式的稀疏矩阵
稀疏矩阵工具	构建，保存，加载以及识别稀疏矩阵的各种函数
其他	包含压缩稀疏图例程，稀疏线性代数等子模块，以及一些异常处理方法
这里有个需要注意的地方是稀疏数组和稀疏矩阵的区别。
这两个类别中的很多函数名称也类似，比如：bsr_array和bsr_matrix，coo_array和coo_matrix等等。

只要区别在于：
***_matrix类的函数是一种基于Compressed Sparse Row（CSR）和Compressed Sparse Column（CSC）格式的块稀疏矩阵表示方法。
它使用一个字典来存储非零元素，其中每个元素对应于一个包含三个值的元组，分别表示该元素的行索引、列索引和非零元素的值。
这种数据结构可以提供更好的计算性能和内存使用效率，特别适合于大规模的块稀疏矩阵计算。

而***_array 类的函数虽然类似于***_matrix的数据结构，但它允许更大的灵活性。
***_array 可以表示任意的稀疏数组，而不仅仅是块稀疏矩阵。
它使用一个具有三个数组的元组来表示稀疏数组，其中第一个数组存储行索引，第二个数组存储列索引，第三个数组存储非零元素的值。
这种数据结构适用于更通用的稀疏数组计算，但可能不如***_matrix高效。

总之，***_matrix和***_array都是用于表示块稀疏矩阵或稀疏数组的数据结构。
***_matrix更适合于大规模的块稀疏矩阵计算，而***_array适用于更通用的稀疏数组计算。

2. 使用示例
稀疏矩阵之所以成为单独的一个模块，是因为它的稀疏的特性在很多领域多有广泛的应用。
scipy.sparse子模块中提供了大概7种：

csc_matrix: 压缩稀疏列格式（Compressed Sparse Column）
csr_matrix: 压缩稀疏行格式（Compressed Sparse Row）
bsr_matrix: 块稀疏行格式（Block Sparse Row）
lil_matrix: 列表格式的列表（List of Lists format）
dok_matrix: 键格式字典（Dictionary of Keys）
coo_matrix: 坐标格式（又名 IJV，三元组格式）
dia_matrix: 对角线格式（DIAgonal format）
2.1. 使用稀疏矩阵
稀疏矩阵其实在运算上和使用普通矩阵一样。
首先，构造一个创建矩阵的方法create_matrix，这个方法会生成一个10x10的矩阵，
方法的参数N表示随机在矩阵的N个位置中生成值。
"""
from scipy import sparse
import numpy as np
import sys


# 创建一个10x10矩阵，其中有值的元素不超过N个
def create_matrix(N):
    data = np.zeros((10, 10))

    for _ in range(N):
        row = np.random.randint(0, 10, 1)
        col = np.random.randint(0, 10, 1)
        data[row, col] = np.random.randint(1, 100, 1)

    return data


# create_matrix创建的是普通矩阵，我们将生成的矩阵转换为稀疏矩阵后，计算方式差不多。

# 创建两个普通矩阵
m1 = create_matrix(8)
m2 = create_matrix(6)

# 计算点积
m1.dot(m2)  # 返回m1和m2的点积结果

# 将普通矩阵变为稀疏矩阵
# （这里的演示用了7种类型中的一种bsr）
d1 = sparse.bsr_matrix(m1)
d2 = sparse.bsr_matrix(m2)

# 计算点积后，用toarray方法转换为二维数组
d1.dot(d2).toarray()
"""从上面的代码可以看出，用scipy.sparse中的稀疏矩阵和使用一般矩阵差不多。

2.2. 稀疏矩阵的性能
我们使用稀疏矩阵，就是因为其运算性能比使用一般矩阵强，否则还不如直接用一般矩阵。
下面，简单测试下scipy.sparse模块下稀疏矩阵的性能。

先看其内存占用是否有减少，为了让性能差别能显著看出，
先扩大测试矩阵为 1000x1000。"""


def create_matrix(N):
    data = np.zeros((1000, 1000))

    for _ in range(N):
        row = np.random.randint(0, 1000, 1)
        col = np.random.randint(0, 1000, 1)
        data[row, col] = np.random.randint(1, 100, 1)

    return data


m1 = create_matrix(8)
m2 = create_matrix(6)

d1 = sparse.csr_matrix(m1)
d2 = sparse.csr_matrix(m2)

print("一般矩阵 m1 占用的空间：{}".format(sys.getsizeof(m1)))
print("一般矩阵 m2 占用的空间：{}".format(sys.getsizeof(m2)))
print("一般矩阵 d1 占用的空间：{}".format(sys.getsizeof(d1)))
print("一般矩阵 d2 占用的空间：{}".format(sys.getsizeof(d2)))
"""# 运行结果：
一般矩阵 m1 占用的空间：8000128
一般矩阵 m2 占用的空间：8000128
一般矩阵 d1 占用的空间：56
一般矩阵 d2 占用的空间：56
可以看出占用的空间明显缩小了。

再看点积的运算性能：（运行10轮，每轮100次）"""

# %%timeit -r 10 -n 100
m1.dot(m2)
"""# 运行结果：
10.6 ms ± 136 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)
稀疏矩阵的点积运算："""

# %%timeit -r 10 -n 100
d1.dot(d2)
"""# 运行结果：
137 µs ± 14.3 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)
可以看出，运算性能差别非常大，一个是毫秒级别（10.6ms）的，一个是微秒级别（137 µs）的。

3. 总结
稀疏矩阵在矩阵中只是一种特殊的矩阵，然而在实际应用领域中，却应用极广，比如：
在数值计算中，可以用于解决大规模线性代数方程组、大规模非线性方程组和非线性优化问题，以及求解大规模约束规划问题。

在模式识别中，如人脸识别、手写数字识别、文本分类等任务，可用于表示高维数据，提取特征并进行降维，提高识别准确率和计算效率。

在推荐系统中，处理大量用户和物品的数据时，稀疏矩阵可以有效地表示这些数据。

在社交网络中，因为一般社交关系都是稀疏的，所以可用于分析社交网络的结构和行为，例如社区检测、影响力传播。

此外，还可以用在计算机视觉，自然语言处理，生物信息学等等领域。
所以，研究稀疏矩阵有其重要的实际意义。"""
